/*
\copyright  Copyright (c) 2023 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\version    
\file       peer_sig_contiguous_data.c
\brief      
*/

#include "peer_sig_contiguous_data.h"
#include "peer_sig_contiguous_data_descriptor.h"

#include <peer_signalling.h>
#include <panic.h>
#include <logging.h>
#include <limits.h>


#define DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA  DEBUG_LOG_ERROR

typedef struct
{
    uint16 num_bytes_consumed;
    peer_sig_contiguous_data_t received;
} peer_sig_contiguous_received_t;

static peer_sig_contiguous_received_t *peer_sig_contiguous_data_received = NULL;

static uint32 peerSigContiguousData_GetStaticDataSize(const void *object,
                             const marshal_member_descriptor_t *member,
                             uint32 arrayElement);

static const marshal_type_descriptor_t marshal_type_descriptor_peer_sig_contiguous_data_header_t =
    MAKE_MARSHAL_TYPE_DEFINITION_BASIC(peer_sig_contiguous_data_header_t);

static const marshal_member_descriptor_t member_descriptors_peer_sig_contiguous_data[] =
{
    MAKE_MARSHAL_MEMBER(peer_sig_contiguous_data_t, peer_sig_contiguous_data_header_t, header),
    MAKE_MARSHAL_MEMBER(peer_sig_contiguous_data_t, uint8, data_length),
    MAKE_MARSHAL_MEMBER_ARRAY(peer_sig_contiguous_data_t, uint8, data, 1),
};

static const marshal_type_descriptor_dynamic_t marshal_type_descriptor_peer_sig_contiguous_data =
    MAKE_MARSHAL_TYPE_DEFINITION_HAS_DYNAMIC_ARRAY(
        peer_sig_contiguous_data_t,
        member_descriptors_peer_sig_contiguous_data,
        peerSigContiguousData_GetStaticDataSize);

/* Use xmacro to expand type table as array of type descriptors */
#define EXPAND_AS_TYPE_DEFINITION(type) (const marshal_type_descriptor_t *)&marshal_type_descriptor_##type,
const marshal_type_descriptor_t * const descriptor_peer_sig_contiguous_data[NUMBER_OF_MARSHAL_OBJECT_TYPES] = {
    MARSHAL_COMMON_TYPES_TABLE(EXPAND_AS_TYPE_DEFINITION)
    MARSHAL_TYPES_TABLE(EXPAND_AS_TYPE_DEFINITION)
};
#undef EXPAND_AS_TYPE_DEFINITION

static uint32 peerSigContiguousData_GetStaticDataSize(const void *object,
                             const marshal_member_descriptor_t *member,
                             uint32 arrayElement)
{
    UNUSED(member);
    UNUSED(arrayElement);

    peer_sig_contiguous_data_t* peer_sig_contiguous_data = (peer_sig_contiguous_data_t*)object;
    return peer_sig_contiguous_data->data_length;
}

static void* peerSigContiguousData_CreateMessage(uint16 total_data_length, const uint8* data, uint16 data_size)
{
    peer_sig_contiguous_data_t* temp = (peer_sig_contiguous_data_t*)PanicUnlessMalloc(sizeof(peer_sig_contiguous_data_t)+data_size);
    temp->header.total_data_length = total_data_length;
    memcpy(temp->data, data, data_size);
    temp->data_length = data_size;
    return temp;
}

static void peerSigContiguousData_CopyReceivedDataToBuffer(const peer_sig_contiguous_data_t* rx)
{
    if (peer_sig_contiguous_data_received == NULL)
    {
        peer_sig_contiguous_data_received = (peer_sig_contiguous_received_t*)PanicUnlessMalloc(sizeof(peer_sig_contiguous_received_t)+rx->header.total_data_length);
        peer_sig_contiguous_data_received->received.header.total_data_length = rx->header.total_data_length;
        memcpy(peer_sig_contiguous_data_received->received.data, rx->data, rx->data_length);
        peer_sig_contiguous_data_received->received.data_length = 0; /* not used */
        peer_sig_contiguous_data_received->num_bytes_consumed = 0;
        DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA("PeerSigContiguousData_Receive: Create receive buffer length %d", rx->header.total_data_length);
    }

    DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA("PeerSigContiguousData_Receive: Add to receive buffer at offset %d", peer_sig_contiguous_data_received->num_bytes_consumed);
    memcpy(&peer_sig_contiguous_data_received->received.data[peer_sig_contiguous_data_received->num_bytes_consumed] , rx->data, rx->data_length);
    peer_sig_contiguous_data_received->num_bytes_consumed += rx->data_length;
    DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA("PeerSigContiguousData_Receive: New buffered data, consumed %d Total length %d", peer_sig_contiguous_data_received->num_bytes_consumed, peer_sig_contiguous_data_received->received.header.total_data_length);
}

void PeerSigContiguousData_Register(Task task, peerSigMsgChannel channel)
{
    DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA("PeerSigContiguousData_Register channel=enum:peerSigMsgChannel:%d", channel);
    appPeerSigClientRegister(task);
    appPeerSigMarshalledMsgChannelTaskRegister(task,
                                               channel,
                                               descriptor_peer_sig_contiguous_data,
                                               NUMBER_OF_MARSHAL_OBJECT_TYPES);
}

uint16 PeerSigContiguousData_GetDataLength(PEER_SIG_MARSHALLED_MSG_CHANNEL_RX_IND_T* ind)
{
    PanicFalse(ind->type == MARSHAL_TYPE_peer_sig_contiguous_data);
    peer_sig_contiguous_data_t* temp = ((peer_sig_contiguous_data_t*)ind->msg);
    return temp->header.total_data_length;
}

void PeerSigContiguousData_GetReceivedData(PEER_SIG_MARSHALLED_MSG_CHANNEL_RX_IND_T* ind, void* rx_buffer)
{
    PanicFalse(ind->type == MARSHAL_TYPE_peer_sig_contiguous_data);
    peer_sig_contiguous_data_t* temp = ((peer_sig_contiguous_data_t*)ind->msg);

    DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA("PeerSigContiguousData_Receive: PEER_SIG_MARSHALLED_MSG_CHANNEL_RX_IND data_length %d total_length %d data %p ", temp->data_length, temp->header.total_data_length, temp->data);
    if (temp->header.total_data_length > temp->data_length)
    {
        peerSigContiguousData_CopyReceivedDataToBuffer(temp);

        if (peer_sig_contiguous_data_received->num_bytes_consumed >= peer_sig_contiguous_data_received->received.header.total_data_length)
        {
            DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA("PeerSigContiguousData_Receive: Copy receive buffer length %d to data_addr %p", peer_sig_contiguous_data_received->received.header.total_data_length, peer_sig_contiguous_data_received->received.data);
            memcpy(rx_buffer, peer_sig_contiguous_data_received->received.data, peer_sig_contiguous_data_received->received.header.total_data_length);
            free(peer_sig_contiguous_data_received);
            peer_sig_contiguous_data_received = NULL;
        }
    }
    else
    {
        DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA("PeerSigContiguousData_Receive: PEER_SIG_MARSHALLED_MSG_CHANNEL_RX_IND Copy length %d to data_addr %p", temp->data_length, temp->data);
        memcpy(rx_buffer, (void*)temp->data, temp->data_length);
    }
    
    /* Free unmarshalled message after use. */
    free(ind->msg);
}

void PeerSigContiguousData_Transmit(Task task, peerSigMsgChannel channel, void* data_addr, uint16 data_size)
{
    if(appPeerSigIsConnected())    
    {
        uint16 size_data_transmitted = 0;
        uint16 total_data_length = data_size;

        while (data_size != 0)
        {
            uint16 size_data_to_transmit = (data_size > UCHAR_MAX) ? UCHAR_MAX : data_size;
            uint8 *data_to_transmit = data_addr;

            data_to_transmit += size_data_transmitted;

            DEBUG_LOG_PEER_SIG_CONTIGUOUS_DATA("PeerSigContiguousData_Transmit: send peer data_to_transmit %p size %d channel %d", data_to_transmit, size_data_to_transmit, channel);
            appPeerSigMarshalledMsgChannelTx(task,
                                            channel,
                                            peerSigContiguousData_CreateMessage(total_data_length, data_to_transmit, size_data_to_transmit),
                                            MARSHAL_TYPE_peer_sig_contiguous_data);
            data_size -= size_data_to_transmit;
            size_data_transmitted += size_data_to_transmit;
        }
    }
}

void PeerSigContiguousData_PeerConnectionStatusChange(PEER_SIG_CONNECTION_IND_T* ind)
{
    if (ind->status == peerSigStatusLinkLoss || ind->status == peerSigStatusDisconnected)
    {
        if (peer_sig_contiguous_data_received != NULL)
        {
            free(peer_sig_contiguous_data_received);
            peer_sig_contiguous_data_received = NULL;
        }
    }
}
